home *** CD-ROM | disk | FTP | other *** search
/ CICA 1995 August / CICA - The Ultimate Collection of Shareware for Windows (Disc 2) (August 1995).iso / disc2 / nt / source.exe / POSIX / ELVIS / CUT.C < prev    next >
C/C++ Source or Header  |  1992-09-26  |  14KB  |  669 lines

  1. /* cut.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains function which manipulate the cut buffers. */
  12.  
  13. #include "config.h"
  14. #include "vi.h"
  15. #if TURBOC
  16. #include <process.h>        /* needed for getpid */
  17. #endif
  18. #if TOS
  19. #include <osbind.h>
  20. #define    rename(a,b)    Frename(0,a,b)
  21. #endif
  22.  
  23. # define NANNONS    9    /* number of annonymous buffers */
  24.  
  25. static struct cutbuf
  26. {
  27.     short    *phys;    /* pointer to an array of #s of BLKs containing text */
  28.     int    nblks;    /* number of blocks in phys[] array */
  29.     int    start;    /* offset into first block of start of cut */
  30.     int    end;    /* offset into last block of end of cut */
  31.     int    fd;    /* fd of tmp file, or -1 to use tmpfd */
  32.     char    lnmode;    /* boolean: line-mode cut? (as opposed to char-mode) */
  33. }
  34.     named[27],    /* cut buffers "a through "z and ". */
  35.     annon[NANNONS];    /* annonymous cut buffers */
  36.  
  37. static char    cbname;    /* name chosen for next cut/paste operation */
  38.  
  39.  
  40. #ifndef NO_RECYCLE
  41. /* This function builds a list of all blocks needed in the current tmp file
  42.  * for the contents of cut buffers.
  43.  * !!! WARNING: if you have more than ~450000 bytes of text in all of the
  44.  * cut buffers, then this will fail disastrously, because buffer overflow
  45.  * is *not* allowed for.
  46.  */
  47. int cutneeds(need)
  48.     BLK        *need;    /* this is where we deposit the list */
  49. {
  50.     struct cutbuf    *cb;    /* used to count through cut buffers */
  51.     int        i;    /* used to count through blocks of a cut buffer */
  52.     int        n;    /* total number of blocks in list */
  53.  
  54.     n = 0;
  55.  
  56.     /* first the named buffers... */
  57.     for (cb = named; cb < &named[27]; cb++)
  58.     {
  59.         if (cb->fd > 0)
  60.             continue;
  61.  
  62.         for (i = cb->nblks; i-- > 0; )
  63.         {
  64.             need->n[n++] = cb->phys[i];
  65.         }
  66.     }
  67.  
  68.     /* then the anonymous buffers */
  69.     for (cb = annon; cb < &annon[NANNONS]; cb++)
  70.     {
  71.         if (cb->fd > 0)
  72.             continue;
  73.  
  74.         for (i = cb->nblks; i-- > 0; )
  75.         {
  76.             need->n[n++] = cb->phys[i];
  77.         }
  78.     }
  79.  
  80.     return n;
  81. }
  82. #endif
  83.  
  84. /* This function frees a cut buffer */
  85. static void cutfree(buf)
  86.     struct cutbuf    *buf;
  87. {
  88.     char    cutfname[50];
  89.     int    i;
  90.  
  91.     /* return immediately if the buffer is already empty */
  92.     if (buf->nblks <= 0)
  93.     {
  94.         return;
  95.     }
  96.  
  97.     /* else free up stuff */
  98.     buf->nblks = 0;
  99. #ifdef DEBUG
  100.     if (!buf->phys)
  101.         msg("cutfree() tried to free an NULL buf->phys pointer.");
  102. #endif
  103.     free((char *)buf->phys);
  104.  
  105.     /* see if anybody else needs this tmp file */
  106.     if (buf->fd >= 0)
  107.     {
  108.         for (i = 0; i < 27; i++)
  109.         {
  110.             if (named[i].nblks > 0 && named[i].fd == buf->fd)
  111.             {
  112.                 break;
  113.             }
  114.         }
  115.     }
  116.  
  117.     /* if nobody else needs it, then discard the tmp file */
  118.     if (buf->fd >= 0 && i == 27)
  119.     {
  120.         close(buf->fd);
  121. #if MSDOS || TOS
  122.         strcpy(cutfname, o_directory);
  123.         if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
  124.             cutfname[i++]=SLASH;
  125.         sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
  126. #else
  127.         sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
  128. #endif
  129.         unlink(cutfname);
  130.     }
  131. }
  132.  
  133. /* This function is called when we are about to abort a tmp file.  If any
  134.  * cut buffers still need the file, then a copy of the file should be
  135.  * created for use by the cut buffers.
  136.  *
  137.  * To minimize the number of extra files lying around, only named cut buffers
  138.  * are preserved in a file switch; the annonymous buffers just go away.
  139.  */
  140. void cutswitch(tmpname)
  141.     char    *tmpname; /* name of the tmp file */
  142. {
  143.     char    cutfname[50];    /* used to build a new name for the tmp file */
  144.     int    fd;        /* a new fd for the current tmp file */
  145.     int    i;
  146. #if MSDOS || TOS
  147.     int    j;
  148. #endif
  149.  
  150.     /* discard all annonymous cut buffers */
  151.     for (i = 0; i < NANNONS; i++)
  152.     {
  153.         cutfree(&annon[i]);
  154.     }
  155.  
  156.     /* find the first named buffer that uses this tmp file */
  157.     for (i = 0; i < 27; i++)
  158.     {
  159.         if (named[i].nblks > 0 && named[i].fd < 0)
  160.         {
  161.             break;
  162.         }
  163.     }
  164.  
  165.     /* if none of them use this tmp file, then we're done */
  166.     if (i == 27)
  167.     {
  168.         return;
  169.     }
  170.  
  171.     /* else we'll need this file and an fd a little longer */
  172. #if MSDOS || TOS
  173.     strcpy(cutfname, o_directory);
  174.     if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
  175.         cutfname[j++]=SLASH;
  176.     close(tmpfd);
  177.     fd = open(tmpname, O_RDONLY|O_BINARY);
  178.     close(fd);
  179.     sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
  180.     rename(tmpname, cutfname);
  181.     fd = open(cutfname, O_RDONLY|O_BINARY);
  182.     tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
  183. #else
  184.     fd = dup(tmpfd);
  185. # if OSK
  186.     sprintf(cutfname, CUTNAME, "", getpid(), fd);
  187.     if (!link(tmpname, &cutfname[1])) /* skip slash */
  188.         unlink(tmpname);
  189. # else    
  190.     sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
  191.     link(tmpname, cutfname) || unlink(tmpname);
  192. # endif
  193. #endif
  194.  
  195.     /* have all cut buffers use the new fd instead */
  196.     for (; i < 27; i++)
  197.     {
  198.         if (named[i].nblks > 0 && named[i].fd < 0)
  199.         {
  200.             named[i].fd = fd;
  201.         }
  202.     }
  203. }
  204.  
  205. /* This function should be called just before termination of vi */
  206. void cutend()
  207. {
  208.     int    i;
  209.  
  210.     /* free all named cut buffers, since they might be forcing an older
  211.      * tmp file to be retained.
  212.      */
  213.     for (i = 0; i < 27; i++)
  214.     {
  215.         cutfree(&named[i]);
  216.     }
  217. }
  218.  
  219.  
  220. /* This function is used to select the cut buffer to be used next */
  221. void cutname(name)
  222.     int    name;    /* a single character */
  223. {
  224.     cbname = name;
  225. }
  226.  
  227.  
  228.  
  229.  
  230. /* This function copies a selected segment of text to a cut buffer */
  231. void cut(from, to)
  232.     MARK    from;        /* start of text to cut */
  233.     MARK    to;        /* end of text to cut */
  234. {
  235.     int        first;    /* logical number of first block in cut */
  236.     int        last;    /* logical number of last block used in cut */
  237.     long        line;    /* a line number */
  238.     int        lnmode;    /* boolean: will this be a line-mode cut? */
  239.     MARK        delthru;/* end of text temporarily inserted for apnd */
  240.     REG struct cutbuf *cb;
  241.     REG long    l;
  242.     REG int        i;
  243.     REG char    *scan;
  244.     char        *blkc;
  245.  
  246.     /* detect whether this must be a line-mode cut or char-mode cut */
  247.     if (markidx(from) == 0 && markidx(to) == 0)
  248.         lnmode = TRUE;
  249.     else
  250.         lnmode = FALSE;
  251.  
  252.     /* by default, we don't "delthru" anything */
  253.     delthru = MARK_UNSET;
  254.  
  255.     /* decide which cut buffer to use */
  256.     if (!cbname)
  257.     {
  258.         /* free up the last annonymous cut buffer */
  259.         cutfree(&annon[NANNONS - 1]);
  260.  
  261.         /* shift the annonymous cut buffers */
  262.         for (i = NANNONS - 1; i > 0; i--)
  263.         {
  264.             annon[i] = annon[i - 1];
  265.         }
  266.  
  267.         /* use the first annonymous cut buffer */
  268.         cb = annon;
  269.         cb->nblks = 0;
  270.     }
  271.     else if (cbname >= 'a' && cbname <= 'z')
  272.     {
  273.         cb = &named[cbname - 'a'];
  274.         cutfree(cb);
  275.     }
  276. #ifndef CRUNCH
  277.     else if (cbname >= 'A' && cbname <= 'Z')
  278.     {
  279.         cb = &named[cbname - 'A'];
  280.         if (cb->nblks > 0)
  281.         {
  282.             /* resolve linemode/charmode differences */
  283.             if (!lnmode && cb->lnmode)
  284.             {
  285.                 from &= ~(BLKSIZE - 1);
  286.                 if (markidx(to) != 0 || to == from)
  287.                 {
  288.                     to = to + BLKSIZE - markidx(to);
  289.                 }
  290.                 lnmode = TRUE;
  291.             }
  292.  
  293.             /* insert the old cut-buffer before the new text */
  294.             mark[28] = to;
  295.             delthru = paste(from, FALSE, TRUE);
  296.             if (delthru == MARK_UNSET)
  297.             {
  298.                 return;
  299.             }
  300.             delthru++;
  301.             to = mark[28];
  302.         }
  303.         cutfree(cb);
  304.     }
  305. #endif /* not CRUNCH */
  306.     else if (cbname == '.')
  307.     {
  308.         cb = &named[26];
  309.         cutfree(cb);
  310.     }
  311.     else
  312.     {
  313.         msg("Invalid cut buffer name: \"%c", cbname);
  314.         cbname = '\0';
  315.         return;
  316.     }
  317.     cbname = '\0';
  318.     cb->fd = -1;
  319.  
  320.     /* detect whether we're doing a line mode cut */
  321.     cb->lnmode = lnmode;
  322.  
  323.     /* ---------- */
  324.  
  325.     /* Reporting... */    
  326.     if (markidx(from) == 0 && markidx(to) == 0)
  327.     {
  328.         rptlines = markline(to) - markline(from);
  329.         rptlabel = "yanked";
  330.     }
  331.  
  332.     /* ---------- */
  333.  
  334.     /* make sure each block has a physical disk address */
  335.     blksync();
  336.  
  337.     /* find the first block in the cut */
  338.     line = markline(from);
  339.     for (first = 1; line > lnum[first]; first++)
  340.     {
  341.     }
  342.  
  343.     /* fetch text of the block containing that line */
  344.     blkc = scan = blkget(first)->c;
  345.  
  346.     /* find the mark in the block */
  347.     for (l = lnum[first - 1]; ++l < line; )
  348.     {
  349.         while (*scan++ != '\n')
  350.         {
  351.         }
  352.     }
  353.     scan += markidx(from);
  354.  
  355.     /* remember the offset of the start */
  356.     cb->start = scan - blkc;
  357.  
  358.     /* ---------- */
  359.  
  360.     /* find the last block in the cut */
  361.     line = markline(to);
  362.     for (last = first; line > lnum[last]; last++)
  363.     {
  364.     }
  365.  
  366.     /* fetch text of the block containing that line */
  367.     if (last != first)
  368.     {
  369.         blkc = scan = blkget(last)->c;
  370.     }
  371.     else
  372.     {
  373.         scan = blkc;
  374.     }
  375.  
  376.     /* find the mark in the block */
  377.     for (l = lnum[last - 1]; ++l < line; )
  378.     {
  379.         while (*scan++ != '\n')
  380.         {
  381.         }
  382.     }
  383.     if (markline(to) <= nlines)
  384.     {
  385.         scan += markidx(to);
  386.     }
  387.  
  388.     /* remember the offset of the end */
  389.     cb->end = scan - blkc;
  390.  
  391.     /* ------- */
  392.  
  393.     /* remember the physical block numbers of all included blocks */
  394.     cb->nblks = last - first;
  395.     if (cb->end > 0)
  396.     {
  397.         cb->nblks++;
  398.     }
  399. #ifdef lint
  400.     cb->phys = (short *)0;
  401. #else
  402.     cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
  403. #endif
  404.     for (i = 0; i < cb->nblks; i++)
  405.     {
  406.         cb->phys[i] = hdr.n[first++];
  407.     }
  408.  
  409. #ifndef CRUNCH
  410.     /* if we temporarily inserted text for appending, then delete that
  411.      * text now -- before the user sees it.
  412.      */
  413.     if (delthru)
  414.     {
  415.         line = rptlines;
  416.         delete(from, delthru);
  417.         rptlines = line;
  418.         rptlabel = "yanked";
  419.     }
  420. #endif /* not CRUNCH */
  421. }
  422.  
  423.  
  424. static void readcutblk(cb, blkno)
  425.     struct cutbuf    *cb;
  426.     int        blkno;
  427. {
  428.     int        fd;    /* either tmpfd or cb->fd */
  429.  
  430.     /* decide which fd to use */
  431.     if (cb->fd >= 0)
  432.     {
  433.         fd = cb->fd;
  434.     }
  435.     else
  436.     {
  437.         fd = tmpfd;
  438.     }
  439.  
  440.     /* get the block */
  441.     lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
  442.     if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
  443.     {
  444.         msg("Error reading back from tmp file for pasting!");
  445.     }
  446. }
  447.  
  448.  
  449. /* This function inserts text from a cut buffer, and returns the MARK where
  450.  * insertion ended.  Return MARK_UNSET on errors.
  451.  */
  452. MARK paste(at, after, retend)
  453.     MARK    at;    /* where to insert the text */
  454.     int    after;    /* boolean: insert after mark? (rather than before) */
  455.     int    retend;    /* boolean: return end of text? (rather than start) */
  456. {
  457.     REG struct cutbuf    *cb;
  458.     REG int            i;
  459.  
  460.     /* decide which cut buffer to use */
  461.     if (cbname >= 'A' && cbname <= 'Z')
  462.     {
  463.         cb = &named[cbname - 'A'];
  464.     }
  465.     else if (cbname >= 'a' && cbname <= 'z')
  466.     {
  467.         cb = &named[cbname - 'a'];
  468.     }
  469.     else if (cbname >= '1' && cbname <= '9')
  470.     {
  471.         cb = &annon[cbname - '1'];
  472.     }
  473.     else if (cbname == '.')
  474.     {
  475.         cb = &named[26];
  476.     }
  477.     else if (!cbname)
  478.     {
  479.         cb = annon;
  480.     }
  481.     else
  482.     {
  483.         msg("Invalid cut buffer name: \"%c", cbname);
  484.         cbname = '\0';
  485.         return MARK_UNSET;
  486.     }
  487.  
  488.     /* make sure it isn't empty */
  489.     if (cb->nblks == 0)
  490.     {
  491.         if (cbname)
  492.             msg("Cut buffer \"%c is empty", cbname);
  493.         else
  494.             msg("Cut buffer is empty");
  495.         cbname = '\0';
  496.         return MARK_UNSET;
  497.     }
  498.     cbname = '\0';
  499.  
  500.     /* adjust the insertion MARK for "after" and line-mode cuts */
  501.     if (cb->lnmode)
  502.     {
  503.         at &= ~(BLKSIZE - 1);
  504.         if (after)
  505.         {
  506.             at += BLKSIZE;
  507.         }
  508.     }
  509.     else if (after)
  510.     {
  511.         /* careful! if markidx(at) == 0 we might be pasting into an
  512.          * empty line -- so we can't blindly increment "at".
  513.          */
  514.         if (markidx(at) == 0)
  515.         {
  516.             pfetch(markline(at));
  517.             if (plen != 0)
  518.             {
  519.                 at++;
  520.             }
  521.         }
  522.         else
  523.         {
  524.             at++;
  525.         }
  526.     }
  527.  
  528.     /* put a copy of the "at" mark in the mark[] array, so it stays in
  529.      * sync with changes made via add().
  530.      */
  531.     mark[27] = at;
  532.  
  533.     /* simple one-block paste? */
  534.     if (cb->nblks == 1)
  535.     {
  536.         /* get the block */
  537.         readcutblk(cb, 0);
  538.  
  539.         /* isolate the text we need within it */
  540.         if (cb->end)
  541.         {
  542.             tmpblk.c[cb->end] = '\0';
  543.         }
  544.  
  545.         /* insert it */
  546.         ChangeText
  547.         {
  548.             add(at, &tmpblk.c[cb->start]);
  549.         }
  550.     }
  551.     else
  552.     {
  553.         /* multi-block paste */
  554.  
  555.         ChangeText
  556.         {
  557.             i = cb->nblks - 1;
  558.  
  559.             /* add text from the last block first */
  560.             if (cb->end > 0)
  561.             {
  562.                 readcutblk(cb, i);
  563.                 tmpblk.c[cb->end] = '\0';
  564.                 add(at, tmpblk.c);
  565.                 i--;
  566.             }
  567.  
  568.             /* add intervening blocks */
  569.             while (i > 0)
  570.             {
  571.                 readcutblk(cb, i);
  572.                 add(at, tmpblk.c);
  573.                 i--;
  574.             }
  575.  
  576.             /* add text from the first cut block */
  577.             readcutblk(cb, 0);
  578.             add(at, &tmpblk.c[cb->start]);
  579.         }
  580.     }
  581.  
  582.     /* Reporting... */
  583.     rptlines = markline(mark[27]) - markline(at);
  584.     rptlabel = "pasted";
  585.  
  586.     /* return the mark at the beginning/end of inserted text */
  587.     if (retend)
  588.     {
  589.         return mark[27] - 1L;
  590.     }
  591.     return at;
  592. }
  593.  
  594.  
  595.  
  596.  
  597. #ifndef NO_AT
  598.  
  599. /* This function copies characters from a cut buffer into a string.
  600.  * It returns the number of characters in the cut buffer.  If the cut
  601.  * buffer is too large to fit in the string (i.e. if cb2str() returns
  602.  * a number >= size) then the characters will not have been copied.
  603.  * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
  604.  */
  605. int cb2str(name, buf, size)
  606.     int    name;    /* the name of a cut-buffer to get: a-z only! */
  607.     char    *buf;    /* where to put the string */
  608.     unsigned size;    /* size of buf */
  609. {
  610.     REG struct cutbuf    *cb;
  611.     REG char        *src;
  612.     REG char        *dest;
  613.  
  614.     /* decide which cut buffer to use */
  615.     if (name >= 'a' && name <= 'z')
  616.     {
  617.         cb = &named[name - 'a'];
  618.     }
  619.     else
  620.     {
  621.         return -1;
  622.     }
  623.  
  624.     /* if the buffer is empty, return 0 */
  625.     if (cb->nblks == 0)
  626.     {
  627.         return 0;
  628.     }
  629.  
  630.     /* !!! if not a single-block cut, then fail */
  631.     if (cb->nblks != 1)
  632.     {
  633.         return size;
  634.     }
  635.  
  636.     /* if too big, return the size now, without doing anything */
  637.     if (cb->end - cb->start >= size)
  638.     {
  639.         return cb->end - cb->start;
  640.     }
  641.  
  642.     /* get the block */
  643.     readcutblk(cb, 0);
  644.  
  645.     /* isolate the string within that blk */
  646.     if (cb->start == 0)
  647.     {
  648.         tmpblk.c[cb->end] = '\0';
  649.     }
  650.     else
  651.     {
  652.         for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
  653.         {
  654.             *dest++ = *src++;
  655.         }
  656.         *dest = '\0';
  657.     }
  658.  
  659.     /* copy the string into the buffer */
  660.     if (buf != tmpblk.c)
  661.     {
  662.         strcpy(buf, tmpblk.c);
  663.     }
  664.  
  665.     /* return the length */
  666.     return cb->end - cb->start;
  667. }
  668. #endif
  669.